增加应用程序和第三方库

Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.


概述

本文档指导用户在Yocto构建系统中添加自定义应用程序和第三方库(以helloworld为例)。通过本文档的步骤,您可以:

  • 将自定义代码集成到Yocto项目中

  • 生成 IPK 安装包

  • 编译和使用第三方库文件

  • 将组件集成到系统镜像中

前置准备

环境要求

  • 已安装必要的开发工具包

  • 已克隆Yocto项目代码

  • 熟悉基本的C语言编程

  • 了解Makefile和BitBake基础

项目目录结构

创建自定义 meta 层,推荐使用以下目录结构:

meta-custom/
├── conf/
│   └── layer.conf                    # 层配置文件
├── recipes-apps/                      # 应用程序配方目录
│   └── helloworld/
│       ├── files/
│       │   ├── helloworld.c          # 应用源码
│       │   ├── Makefile              # 编译脚本
│       │   └── helloworld.service    # systemd服务文件
│       └── helloworld_1.0.bb         # BitBake配方
└── recipes-libs/                      # 库配方目录
    └── lib-helloworld/
        ├── files/
        │   ├── lib_helloworld.c      # 库源码
        │   ├── lib_helloworld.h      # 库头文件
        │   └── Makefile              # 编译脚本
        └── lib-helloworld_1.0.bb     # BitBake配方

添加自定义应用程序

创建层目录结构

创建目录

mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-apps/helloworld/files
mkdir -p ${YOCTO_DIR}/layers/meta-custom/conf

配置自定义层

layers/meta-custom/conf/layer.conf 文件中添加:

BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb"
LAYERDEPENDS_meta-custom = "core"
LAYERSERIES_COMPAT_meta-custom = "kirkstone"

注册层到构建系统

编辑 layers/meta-qcom-distro/conf/bblayers.conf,添加自定义层路径:

BBLAYERS = " \
    ${WORKSPACE}/layers/meta-custom \
"

添加到镜像配方

编辑 layers/meta-quectel/recipes-products/images/qcom-multimedia-image.bbappend,添加:

# 添加应用程序
IMAGE_INSTALL:append = " helloworld"

# 添加库文件(如果需要)
IMAGE_INSTALL:append = " \
    lib-helloworld \
    lib-helloworld-dev \
    lib-helloworld-staticdev \
"

创建应用程序源码

编写主程序

meta-custom/recipes-apps/helloworld/files/ 目录下创建 helloworld.c

// helloworld.c#include <stdio.h>int main() {printf("Hello, world!\n");return 0;}

编写 Makefile

meta-custom/recipes-apps/helloworld/files/ 目录下创建 Makefile

# 使用Yocto提供的编译标志all: helloworld

helloworld: helloworld.c
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<clean:
    rm -f helloworld

.PHONY: all clean

创建systemd服务文件

meta-custom/recipes-apps/helloworld/files/ 目录下创建 helloworld.service

[Unit]Description=Hello World Service[Service]Type=simpleExecStart=/usr/bin/helloworld[Install]WantedBy=multi-user.target

编写 BitBake 配方

meta-custom/recipes-apps/helloworld/ 目录下创建 helloworld_1.0.bb

SUMMARY = "Hello World application"
DESCRIPTION = "A simple Hello World application."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"# 源代码文件列表
SRC_URI = "file://helloworld.c \
           file://Makefile \
           file://helloworld.service"

# 设置工作目录
S = "${WORKDIR}"# 继承 systemd 类
inherit systemd

# 定义 systemd 服务文件名
SYSTEMD_SERVICE:${PN} = "helloworld.service"# 配置阶段(跳过)
do_configure() {
    bbnote "skip do_configure"}# 编译阶段
do_compile() {# 先清理
    oe_runmake -f ${S}/Makefile clean
    # 执行编译
    oe_runmake -f ${S}/Makefile
}# 安装阶段
do_install() {# 创建可执行文件安装目录
    install -d ${D}${bindir}# 安装可执行文件
    install -m 0755 ${S}/helloworld ${D}${bindir}# 创建 systemd 服务目录
    install -d ${D}${systemd_system_unitdir}# 安装服务文件
    install -m 0644 ${S}/helloworld.service ${D}${systemd_system_unitdir}}

构建和安装应用程序

配置构建环境

进入代码工作目录,执行:

source quectel_build/compile/build.sh

方式一:单独编译并安装IPK包

步骤 1:编译应用程序

bitbake helloworld

步骤 2:查找生成的IPK包

find ~/build-qcom-wayland/tmp-glibc/deploy -name "helloworld*.ipk"

输出示例:

/home/quectel/build-qcom-wayland/tmp-glibc/deploy/ipk/armv8-2a/helloworld_1.0-r0_armv8-2a.ipk

步骤 3:推送到设备

adb push helloworld_1.0-r0_armv8-2a.ipk /mnt

步骤 4:安装IPK包

在设备上执行:

# 挂载文件系统为读写模式mount -o remount,rw /usr

# 安装 IPK 包
opkg install /mnt/helloworld_1.0-r0_armv8-2a.ipk

步骤 5:验证安装

/usr/bin/helloworld

预期输出:

Hello, world!

方式二:整编系统镜像

步骤 1:配置构建参数

# 用户可自定义输入参数,从提示的有效选项中选择
buildconfig QSM200UPEMPI QSM200UPEMNARL1A01_BL01BP01K0M01V01_LP5.15.153.01.001_V01 STD

步骤 2:编译整个系统

buildall

步骤 3:打包镜像

buildpackage

步骤 4:烧录镜像

生成的镜像位于 quectel_build/fw/QSM200UPEMNARL1A01_BL01BP01K0M01V01_LP5.15.153.01.001_V01 路径下。

步骤 5:验证安装

烧录完成后,在设备上执行:

/usr/bin/helloworld

预期输出:

Hello, world!

添加第三方库

创建库文件

创建目录

mkdir -p ${YOCTO_DIR}/layers/meta-custom/recipes-libs/lib-helloworld/files

创建库头文件

meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 lib_helloworld.h

// lib_helloworld.h#ifndef LIB_HELLO_H#define LIB_HELLO_Hvoid hello_print(const char* name);#endif

创建库源码

meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 lib_helloworld.c

// lib_helloworld.c#include <stdio.h>#include "lib_helloworld.h"void hello_print(const char* name) {printf("[LIB] Hello, %s!\n", name);}

编写Makefile

meta-custom/recipes-libs/lib-helloworld/files/ 目录下创建 Makefile

LIB_VERSION = 1.0

all:${CC} ${CFLAGS} -fPIC -c lib_helloworld.c -o lib_helloworld.o
    ${CC} ${CFLAGS} -fPIC -shared lib_helloworld.o -o libhello.so.${LIB_VERSION} \
        -Wl,-soname,libhello.so.1 ${LDFLAGS}
    ar rcs libhello.a lib_helloworld.o
    ln -sf libhello.so.${LIB_VERSION} libhello.so
    ln -sf libhello.so.${LIB_VERSION} libhello.so.1

clean:
    rm -f libhello.so* lib_helloworld.o libhello.a

编写 BitBake 配方

meta-custom/recipes-libs/lib-helloworld/ 目录下创建 lib-helloworld_1.0.bb

SUMMARY = "Hello World Library"
SECTION = "libs"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://lib_helloworld.c \
           file://lib_helloworld.h \
           file://Makefile"

S = "${WORKDIR}"

EXTRA_OEMAKE = "'CC=${CC}' 'CFLAGS=${CFLAGS} -fPIC' 'LDFLAGS=${LDFLAGS}'"

do_install() {# 创建目标目录
    install -d ${D}${libdir}
    install -d ${D}${includedir}# 安装共享库及符号链接
    install -m 0755 ${S}/libhello.so.${PV} ${D}${libdir}/
    ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so
    ln -sf libhello.so.${PV} ${D}${libdir}/libhello.so.1# 安装静态库
    install -m 0644 ${S}/libhello.a ${D}${libdir}/# 安装头文件
    install -m 0644 ${S}/lib_helloworld.h ${D}${includedir}/}# 包文件分配
FILES:${PN} = " \
    ${libdir}/libhello.so.1 \
    ${libdir}/libhello.so.${PV} \
"

FILES:${PN}-dev = " \
    ${libdir}/libhello.so \
    ${includedir}/lib_helloworld.h \
"

FILES:${PN}-staticdev = " \
    ${libdir}/libhello.a \
"

构建和使用第三方库

配置构建环境

source quectel_build/compile/build.sh

方式一:单独编译并安装

步骤 1:编译库

bitbake lib-helloworld

步骤 2:查找生成的库文件

find ./* -name "libhello*"

步骤 3:推送到设备

adb push libhello.so libhello.so.1 libhello.so.1.0 libhello.a /usr/lib

步骤 4:修复权限和符号链接

在设备上执行:

# 设置执行权限chmod 755 /usr/lib/libhello.so*
chmod 644 /usr/lib/libhello.a

# 重建符号链接cd /usr/lib
ln -sf libhello.so.1.0 libhello.so.1
ln -sf libhello.so.1 libhello.so

步骤 5:编写测试程序

创建静态库测试程序 static_test_helloworld.c

#include <stdio.h>void hello_print(const char*);int main() {printf("static lib test\n");hello_print("world");return 0;}

创建动态库测试程序 dynamic_test_helloworld.c

#include <stdio.h>void hello_print(const char*);int main() {printf("dynamic lib test\n");hello_print("world");return 0;}

步骤 6:编译并运行测试程序

静态库测试:

gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello

预期输出:

static lib test
[LIB] Hello, world!

动态库测试:

gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello

预期输出:

dynamic lib test
[LIB] Hello, world!

方式二:整编系统镜像

验证库文件

烧录完成后,在设备上检查库文件:

ls -l /usr/lib/libhello*

预期输出:

-rw-r--r--. 2 root root 5002 Jan  1  1970 /usr/lib/libhello.a
lrwxrwxrwx. 1 root root   15 Jul 25 06:26 /usr/lib/libhello.so -> libhello.so.1.0
lrwxrwxrwx. 1 root root   15 Jul 25 06:26 /usr/lib/libhello.so.1 -> libhello.so.1.0
-rwxr-xr-x. 2 root root 5968 Jan  1  1970 /usr/lib/libhello.so.1.0

运行测试程序

静态库测试:

gcc static_test_helloworld.c -lhello -static -o static_hello
./static_hello

预期输出:

static lib test
[LIB] Hello, world!

动态库测试:

gcc dynamic_test_helloworld.c /usr/lib/libhello.so -o dynamic_hello
./dynamic_hello

预期输出:

dynamic lib test
[LIB] Hello, world!

注意事项

  1. 层优先级:确保自定义层的配置正确,避免与其他层冲突

  2. 许可证声明:务必正确设置LICENSE和LIC_FILES_CHKSUM

  3. 文件权限:注意可执行文件和库文件的权限设置

  4. 符号链接:动态库的符号链接必须正确设置

  5. 依赖关系:如果库依赖其他包,需在配方中声明DEPENDS

  6. 版本管理:建议使用版本号管理库文件

总结

通过本文档的指导,您可以在Yocto环境中:

  • 创建自定义应用程序和库

  • 生成 IPK 安装包进行单独部署

  • 将组件集成到系统镜像中

  • 验证和测试自定义组件

掌握这些技能后,您可以根据实际需求扩展和定制系统功能。